package com.kzj.global.schedule;

import com.kzj.domain.*;
import com.kzj.domain.vo.ElectiveCourseVO;
import com.kzj.domain.vo.FreeClassroomDTO;
import com.kzj.domain.vo.ScheduleCourseVO;
import com.kzj.global.constant.GlobalConstant;
import com.kzj.global.exception.MyException;
import com.kzj.global.params.GlobalParams;
import com.kzj.global.re.Result;
import com.kzj.global.utils.GlobalUtil;
import com.kzj.global.utils.IdGeneratorUtil;
import com.kzj.service.*;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.Trigger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.text.DateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.Month;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.ScheduledFuture;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@Data
@Configuration
@Slf4j
public class MySchedule {
    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10); // 设置线程池大小
        return taskScheduler;
    }
    private Map<String, ScheduledFuture<?>> scheduledTasks = new HashMap<>();

    public void createScheduledTask(String taskName, Runnable task, Trigger trigger) {
        destroyScheduledTask(taskName);
        ScheduledFuture<?> scheduledFuture = taskScheduler.schedule(task,trigger);
        scheduledTasks.put(taskName, scheduledFuture);
    }

    public void destroyScheduledTask(String taskName) {
        ScheduledFuture<?> scheduledFuture = scheduledTasks.get(taskName);
        if (scheduledFuture != null) {
            scheduledFuture.cancel(true);
            scheduledTasks.remove(taskName);
        }
    }

    @Autowired
    TaskScheduler taskScheduler;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    EletiveCourseService eletiveCourseService;
    @Autowired
    CourseSelectionInformationService courseSelectionInformationService;
    @Autowired
    TeacherService teacherService;
    @Autowired
    StudentService studentService;
    @Autowired
    ClassroomService classroomService;
    @Autowired
    ExaminationService examinationService;
    @Autowired
    GlobalParams globalParams;
    @Autowired
    StudentExaminationInformationService studentExaminationInformationService;
    @Autowired
    InvigilatorInformationService invigilatorInformationService;
    @Autowired
    GlobalConstant globalConstant;
    @Autowired
    GlobalUtil globalUtil;
    @PostConstruct
    void init(){
        updateDateTime();
        robClassesPreheat();
        stopRobClasses();
        arrangeExaminationScheduled();
    }
    @Scheduled(cron="#{@globalConstant.GET_DATE_TIME_CRON}")
    public void updateDateTime()
    {
        log.info("更新时间");
        DateTimeFormatter format=DateTimeFormatter.ofPattern(GlobalConstant.DATE_TIME_FORMATTER);
        //TODO 更新时间
        String arrangeExaminationDateString = stringRedisTemplate.opsForValue().get(GlobalConstant.ARRANGE_EXAMINATION_KEY);
        String robClassesPreHeatDateString = stringRedisTemplate.opsForValue().get(GlobalConstant.ROB_CLASSES_PREHEAT_KEY);
        String stopRobClassesDateString = stringRedisTemplate.opsForValue().get(GlobalConstant.STOP_ROB_CLASSES_KEY);
        globalParams.setArrangeExaminationDate(LocalDateTime.parse(arrangeExaminationDateString,format));
        globalParams.setRobClassesPreHeatDate(LocalDateTime.parse(robClassesPreHeatDateString,format));
        globalParams.setStopRobClassesDate(LocalDateTime.parse(stopRobClassesDateString,format));
    }

//    @Scheduled(cron="#{globalUtil.getCronByDate(globalParams.getRobClassesPreHeatDate())}")
    public void robClassesPreheat()
    {
        log.info("开启任务-预热课程信息");
        String term=GlobalUtil.getNextTerm();
        String s=stringRedisTemplate.opsForValue().get(GlobalConstant.FLAG_ARRANGE_EXAMINATION + term);
        if(s!=null)
            throw new MyException(GlobalConstant.ROB_CLASSES_NOT_ALLOW);
        stringRedisTemplate.opsForValue().set(GlobalConstant.FLAG_ROB_CLASSES+term,"yes");
        String cron = globalUtil.getCronByDate(globalParams.getRobClassesPreHeatDate());
        createScheduledTask(GlobalConstant.TASK_ROB_CLASSES_PREHEAT,()->{
            log.info("预热课程信息-任务执行开始》》》》》》》》》》》》》》》");
            eletiveCourseService.preheat(term);
            log.info("预热课程信息-任务执行结束《《《《《《《《《《《《《《《");
        },new CronTrigger(cron));
    }
//    @Scheduled(cron = "#{globalUtil.getCronByDate(globalParams.getStopRobClassesDate())}")
    public void stopRobClasses()
    {
        log.info("开启任务-结束抢课");
        String cron=globalUtil.getCronByDate(globalParams.getStopRobClassesDate());
        createScheduledTask(GlobalConstant.TASK_STOP_ROB_CLASSES,()->{
            log.info("结束抢课-任务执行开始》》》》》》》》》》》》》》》");
            Set<String> keys = stringRedisTemplate.keys(GlobalConstant.COURSE_AMOUNT_PREFIX + "*");
            Set<String> keys2 = stringRedisTemplate.keys(GlobalConstant.MAJOR_OPTIONAL_COURSE_ID_PREFIX+"*");
            stringRedisTemplate.delete(keys);
            stringRedisTemplate.delete(keys2);
            log.info("结束抢课-任务执行结束《《《《《《《《《《《《《《《");
        },new CronTrigger(cron));
    }
//    @Scheduled(cron = "#{globalUtil.getCronByDate(globalParams.getArrangeExaminationDate())}")
    public void arrangeExaminationScheduled()
    {
        log.info("开启任务-安排考试");
        String cron=globalUtil.getCronByDate(globalParams.getArrangeExaminationDate());
        createScheduledTask(GlobalConstant.TASK_ARRANGE_EXAMINATION,()->{
            log.info("安排考试-任务执行开始》》》》》》》》》》》》》》》");
            arrangeExamination(GlobalUtil.getNextTerm());
            log.info("安排考试-任务执行结束《《《《《《《《《《《《《《《");
        },new CronTrigger(cron));
    }
    @Transactional
    public void arrangeExamination(String term){
        //TODO 定时安排考试
        String s1 = stringRedisTemplate.opsForValue().get(GlobalConstant.FLAG_ROB_CLASSES + term);
        System.out.println(s1);
        if(s1==null)
        {
            throw new MyException(GlobalConstant.ARRANGE_EXAMINATION_NOT_ALLOW_BEFORE_ROB_CLASSES);
        }
        String s = stringRedisTemplate.opsForValue().get(GlobalConstant.FLAG_ARRANGE_EXAMINATION + term);
        if(s!=null)
        {
            throw new MyException(GlobalConstant.ARRANGE_EXAMINATION_NOT_ALLOW_OVER);
        }
        stringRedisTemplate.opsForValue().set(GlobalConstant.FLAG_ARRANGE_EXAMINATION + GlobalUtil.getNextTerm(),"yes");
        List<InvigilatorInformation> invigilatorInformations=new ArrayList<>();
        List<Examination> examinations=new ArrayList<>();
//        List<ElectiveCourseVO> electiveCourseVOS = eletiveCourseService.query().eq("flag", GlobalConstant.COURSE_OPTIONAL_FLAG).list().stream().map(item->eletiveCourseService.getElectiveCourseVO(item)).collect(Collectors.toList());
        List<ElectiveCourseVO> electiveCourseVOS = eletiveCourseService.query().eq("curriculum_time", term).list().stream().map(item->eletiveCourseService.getElectiveCourseVO(item)).collect(Collectors.toList());
        List<ElectiveCourseVO> subElectiveCourseVOS=new ArrayList<>();
        //TODO 通选课
        for (ElectiveCourseVO electiveCourseVO : electiveCourseVOS) {
            if(electiveCourseVO.getRestrictedMajorIds().equals(GlobalConstant.COURSE_RESTRICTED_MAJORS_GENERAL_ELECTIVE_COURSE))
            {
                Examination examination = new Examination();
//                examination.setId(IdGeneratorUtil.generatorId(Examination.class));
                examination.setClassroomId(electiveCourseVO.getClassroomId());
                examination.setElectiveCourseId(electiveCourseVO.getId());
                String startTime=electiveCourseVO.getEndWeek()+GlobalConstant.COURSE_SCHOOL_TIME_SPLIT_JOIN+electiveCourseVO.getSchooltime();
                examination.setStartTime(startTime);
                examinations.add(examination);
            }
            else{
                subElectiveCourseVOS.add(electiveCourseVO);
            }
        }
        //TODO 延后2周 考试不能安排到第6节
        List<ElectiveCourseVO> subElectiveCourseVOS2=new ArrayList<>();
        for(ElectiveCourseVO subElectiveCourseVO : subElectiveCourseVOS) {
            String[] split = subElectiveCourseVO.getSchooltime().split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
            Integer dayOfWeek=Integer.valueOf(split[0]);
            Integer timeOfDay=Integer.valueOf(split[1]);
            if(timeOfDay>=6)
            {
                subElectiveCourseVOS2.add(subElectiveCourseVO);
                continue;
            }
            Integer week=subElectiveCourseVO.getEndWeek()+GlobalConstant.EXAMINATION_DELAY_WEEK;
            List<CourseSelectionInformation> courseSelectionInformations = courseSelectionInformationService.query().eq("elective_course_id", subElectiveCourseVO.getId()).list();
            List<List<ScheduleCourseVO>> scheduleCourseVOListList;
            boolean flag=true;
            for (CourseSelectionInformation courseSelectionInformation : courseSelectionInformations) {
                 scheduleCourseVOListList = eletiveCourseService.getScheduleCourseVOListList(courseSelectionInformation.getStudentId(), courseSelectionInformation.getSelectTerm(), week);
                if(scheduleCourseVOListList.get(timeOfDay-1).get(dayOfWeek-1).getId()!=null)
                {
                    flag=false;
                    break;
                }

            }

            if(flag)
            {
                String startTime=subElectiveCourseVO.getEndWeek()+GlobalConstant.COURSE_SCHOOL_TIME_SPLIT_JOIN+subElectiveCourseVO.getSchooltime();
                FreeClassroomDTO freeClassroomDTO = new FreeClassroomDTO();
                freeClassroomDTO.setEndWeek(week);
                freeClassroomDTO.setStartWeek(week);
                freeClassroomDTO.setSchooltime(startTime);
                List<Classroom> classrooms = (List<Classroom>)classroomService.getFreeClassroom(freeClassroomDTO).getData();
                if(classrooms==null||classrooms.isEmpty())
                {
                    subElectiveCourseVOS2.add(subElectiveCourseVO);
                }
                else{
                    List<String> collect = classrooms.stream().map(item -> item.getId()).collect(Collectors.toList());
                    String classroomId=collect.get(0);
                    if(collect.contains(subElectiveCourseVO.getClassroomId()))
                    {
                        classroomId=subElectiveCourseVO.getClassroomId();
                    }
                    Examination examination = new Examination();
//                    examination.setId(IdGeneratorUtil.generatorId(Examination.class));
                    examination.setStartTime(startTime);
                    examination.setClassroomId(classroomId);
                    examination.setElectiveCourseId(subElectiveCourseVO.getId());
                    examinations.add(examination);
                }
            }
            else{
                subElectiveCourseVOS2.add(subElectiveCourseVO);
            }
        }
        updateExaminationList(subElectiveCourseVOS2,examinations);
        List<Examination> completeExaminations=new ArrayList<>();
        while(true)
        {
            List<Examination> removeExaminations=new ArrayList<>();
            List<Examination> collect = examinations.stream().filter(new Predicate<Examination>() {
                @Override
                public boolean test(Examination examination) {
                    return !completeExaminations.contains(examination);
                }
            }).collect(Collectors.toList());
            for (Examination examination : collect) {
                List<String> freeTeacherId = getFreeTeacherId(examination.getStartTime(), term);
                if(freeTeacherId.size()<2)
                {
                    removeExaminations.add(examination);
                    continue;
                }
                Collections.shuffle(freeTeacherId);

                InvigilatorInformation invigilatorInformation1 = new InvigilatorInformation();
                examination.setId(IdGeneratorUtil.generatorId(Examination.class));
                invigilatorInformation1.setExaminationId(examination.getId());
                invigilatorInformation1.setTeacherId(freeTeacherId.get(0));
                InvigilatorInformation invigilatorInformation2 = new InvigilatorInformation();
                invigilatorInformation2.setExaminationId(examination.getId());
                invigilatorInformation2.setTeacherId(freeTeacherId.get(1));
                invigilatorInformations.add(invigilatorInformation1);
                invigilatorInformations.add(invigilatorInformation2);
                completeExaminations.add(examination);
            }
            if(removeExaminations.size()>0)
            {
                examinations.removeAll(removeExaminations);
                List<ElectiveCourseVO> collect1 = removeExaminations.stream().map(item -> {
                    String[] split = item.getStartTime().split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
                    Integer week = Integer.valueOf(split[0]);
                    Integer dayOfWeek = Integer.valueOf(split[1]);
                    Integer timeOfDay = Integer.valueOf(split[2]);
                    ElectiveCourseVO electiveCourseVO = new ElectiveCourseVO();
                    electiveCourseVO.setId(item.getElectiveCourseId());
                    electiveCourseVO.setEndWeek(week);
                    electiveCourseVO.setSchooltime(dayOfWeek + GlobalConstant.COURSE_SCHOOL_TIME_SPLIT_JOIN + timeOfDay);
                    return electiveCourseVO;
                }).collect(Collectors.toList());
                updateExaminationList(collect1,examinations);
            }
            else{
                break;
            }
        }
        //
        examinationService.saveBatch(examinations);
        invigilatorInformationService.saveBatch(invigilatorInformations);
        List<StudentExaminationInformation> list=new ArrayList<>();
        for (Examination examination : examinations) {
            List<CourseSelectionInformation> courseSelectionInformations = courseSelectionInformationService.query().eq("elective_course_id", examination.getElectiveCourseId()).list();
            for (int i = 0; i < courseSelectionInformations.size(); i++) {
                CourseSelectionInformation item = courseSelectionInformations.get(i);
                StudentExaminationInformation studentExaminationInformation = new StudentExaminationInformation();
                studentExaminationInformation.setExaminationId(examination.getId());
                studentExaminationInformation.setStudentId(item.getStudentId());
                studentExaminationInformation.setCandidateNumber(i+1);
                list.add(studentExaminationInformation);
            }
        }
        studentExaminationInformationService.saveBatch(list);
    }
    public void  updateExaminationList(List<ElectiveCourseVO> subElectiveCourseVOS,List<Examination> examinations)
    {
        //TODO 特殊处理 考试不能安排到第6节
        for (ElectiveCourseVO electiveCourseVO : subElectiveCourseVOS) {
            String[] split = electiveCourseVO.getSchooltime().split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
            Integer dayOfWeek=Integer.valueOf(split[0]);
            Integer timeOfDay=Integer.valueOf(split[1]);
            Integer week = electiveCourseVO.getEndWeek()+GlobalConstant.EXAMINATION_DELAY_WEEK;
            while(true)
            {
                timeOfDay++;
                if(timeOfDay>=6)
                {
                    timeOfDay=1;
                    dayOfWeek++;
                    if(dayOfWeek>7)
                    {
                        dayOfWeek=1;
                        week++;
                    }
                }
                String startTime=week+GlobalConstant.COURSE_SCHOOL_TIME_SPLIT_JOIN+dayOfWeek+GlobalConstant.COURSE_SCHOOL_TIME_SPLIT_JOIN+timeOfDay;
                List<CourseSelectionInformation> courseSelectionInformations= courseSelectionInformationService.query().eq("elective_course_id", electiveCourseVO.getId()).list();
                List<String> studentIds = courseSelectionInformations.stream().map(item -> item.getStudentId()).collect(Collectors.toList());
                boolean flag=true;
                List<String> noFreeClassroomId=new ArrayList<>();
                for (Examination examination : examinations) {
                    if(examination.getStartTime().equals(startTime))
                    {
                        noFreeClassroomId.add(examination.getClassroomId());
                        List<CourseSelectionInformation>  courseSelectionInformationsMid= courseSelectionInformationService.query().eq("elective_course_id", examination.getElectiveCourseId()).list();
                        List<String> studentIdsMid = courseSelectionInformationsMid.stream().map(item -> item.getStudentId()).collect(Collectors.toList());
                        for (String studentId : studentIds) {
                            if(studentIdsMid.contains(studentId))
                            {
                                flag=false;
                                break;
                            }
                        }
                        if(!flag)
                        {
                            break;
                        }
                    }
                }
                if(!flag)
                {
                    continue;
                }
                for (CourseSelectionInformation courseSelectionInformation : courseSelectionInformations) {
                    List<List<ScheduleCourseVO>> scheduleCourseVOListList = eletiveCourseService.getScheduleCourseVOListList(courseSelectionInformation.getStudentId(), courseSelectionInformation.getSelectTerm(), week);
                    if(scheduleCourseVOListList.get(timeOfDay-1).get(dayOfWeek-1).getId()!=null)
                    {
                        flag=false;
                        break;
                    }

                }

                if(flag)
                {
                    FreeClassroomDTO freeClassroomDTO = new FreeClassroomDTO();
                    freeClassroomDTO.setEndWeek(week);
                    freeClassroomDTO.setStartWeek(week);
                    freeClassroomDTO.setSchooltime(dayOfWeek+GlobalConstant.COURSE_SCHOOL_TIME_SPLIT_JOIN+timeOfDay);
                    List<Classroom> classrooms = (List<Classroom>)classroomService.getFreeClassroom(freeClassroomDTO).getData();
                    if(classrooms==null)
                    {
                        continue;
                    }
                    classrooms=classrooms.stream().filter(new Predicate<Classroom>() {
                        @Override
                        public boolean test(Classroom classroom) {
                            return !noFreeClassroomId.contains(classroom.getId());
                        }
                    }).collect(Collectors.toList());
                    if(classrooms.isEmpty())
                    {
                        continue;
                    }
                    List<String> collect = classrooms.stream().map(item -> item.getId()).collect(Collectors.toList());
                    String classroomId=collect.get(0);
                    if(collect.contains(electiveCourseVO.getClassroomId()))
                    {
                        classroomId=electiveCourseVO.getClassroomId();
                    }
                    Examination examination = new Examination();
//                        examination.setId(IdGeneratorUtil.generatorId(Examination.class));
                    examination.setStartTime(startTime);
                    examination.setClassroomId(classroomId);
                    examination.setElectiveCourseId(electiveCourseVO.getId());
                    examinations.add(examination);
                    break;
                }

            }
        }
    }
    public List<String> getFreeTeacherId(String time,String curriculumTime)
    {
        List<String> teacherIds=new ArrayList<>();
        String[] split = time.split(GlobalConstant.COURSE_SCHOOL_TIME_SPLIT);
        Integer week=Integer.valueOf(split[0]);
        Integer dayOfWeek=Integer.valueOf(split[1]);
        Integer timeOfDay=Integer.valueOf(split[2]);
        List<Teacher> list = teacherService.list();
        for (Teacher teacher : list) {
            List<List<ScheduleCourseVO>> scheduleCourseVOListListByTeacherId = eletiveCourseService.getScheduleCourseVOListListByTeacherId(teacher.getId(), curriculumTime, week);
            if(scheduleCourseVOListListByTeacherId.get(timeOfDay-1).get(dayOfWeek-1).getId()==null)
            {
                teacherIds.add(teacher.getId());
            }
        }
        return teacherIds;
    }
}
